package com.zxj.friend.service.user.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zxj.common.core.contains.Constants;
import com.zxj.common.core.domain.vo.PageVO;
import com.zxj.common.core.enums.ExamListType;
import com.zxj.common.core.enums.ResultCode;
import com.zxj.common.core.utils.ThreadLocalUtil;
import com.zxj.common.security.exception.ServiceException;
import com.zxj.friend.domain.exam.dto.ExamQueryPageDTO;
import com.zxj.friend.domain.exam.entity.Exam;
import com.zxj.friend.domain.exam.vo.ExamVO;
import com.zxj.friend.domain.user.entity.UserExam;
import com.zxj.friend.manager.ExamCacheManager;
import com.zxj.friend.manager.UserExamCacheManager;
import com.zxj.friend.mapper.user.UserExamMapper;
import com.zxj.friend.service.user.IUserExamService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 竞赛报名表 服务实现类
 * </p>
 *
 * @author zxj2022
 * @since 2025-01-08
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class UserExamServiceImpl extends ServiceImpl<UserExamMapper, UserExam> implements IUserExamService {

    private final UserExamCacheManager userExamCacheManager;

    private final ExamCacheManager examCacheManager;

    /**
     * 报名竞赛
     * <li>用户必须登录</li>
     * <li>报名的竞赛必须存在</li>
     * <li>不能重复报名</li>
     * <li>已经开始的竞赛不能报名</li>
     *
     *
     * @param examId 竞赛id
     * @return 是否成功
     */
    @Override
    public boolean enter(Long examId) {
        // 能走到这里说明网关身份验证已经通过, 已经是登录状态, 可以获取到用户id
        Long userId = ThreadLocalUtil.getUserId();
        if (userId == null) {
            log.error("用户未登录");
            throw new ServiceException(ResultCode.FAILED_UNAUTHORIZED);
        }
        // 1. 报名的竞赛必须存在
        Exam exam = Db.getById(examId, Exam.class);
        if (exam == null) {
            log.error("报名的竞赛不存在, examId: {}", examId);
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        // 2. 不能重复报名
        Long count = this.lambdaQuery()
                .eq(UserExam::getExamId, examId)
                .eq(UserExam::getUserId, userId)
                .count();
        if (count > 0) {
            log.error("不能重复报名, examId: {}", examId);
            throw new ServiceException(ResultCode.USER_EXAM_HAS_ENTER);
        }
        // 3. 已经开始的竞赛不能报名
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            log.error("已经开始的竞赛不能报名, examId: {}", examId);
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }

        // 4. 报名 - 数据库存一份, Redis存一份
        UserExam userExam = new UserExam();
        userExam.setExamId(examId);
        userExam.setUserId(userId);
        boolean save = this.save(userExam);
        if (!save) {
            log.error("数据库保存用户竞赛信息失败, examId: {}", examId);
            throw new ServiceException(ResultCode.FAILED);
        }

        // 5. Redis存一份
        userExamCacheManager.addUserExamCache(userId, examId);
        return true;
    }

    /**
     * 分页查询用户报名竞赛列表
     *
     * @param examQueryPageDTO 查询条件
     * @return 用户报名竞赛列表
     */
    @Override
    public PageVO<ExamVO> pageList(ExamQueryPageDTO examQueryPageDTO) {
        Long userId = ThreadLocalUtil.getUserId();
        examQueryPageDTO.setType(ExamListType.USER_EXAM_LIST.getValue());
        Long total = examCacheManager.getListSize(examQueryPageDTO.getType(), userId);
        List<ExamVO> examVOList;
        if (total == null || total <= 0) {
            //从数据库中查询我的竞赛列表
            PageHelper.startPage(examQueryPageDTO.getPageNo(), examQueryPageDTO.getPageSize());
            examVOList = this.baseMapper.selectUserExamList(userId);
            examCacheManager.refreshCache(examQueryPageDTO, userId);
            total = new PageInfo<>(examVOList).getTotal();
        } else {
            examVOList = examCacheManager.getExamVOList(examQueryPageDTO, userId);
            total = examCacheManager.getListSize(examQueryPageDTO.getType(), userId);
        }
        if (CollectionUtil.isEmpty(examVOList)) {
            return PageVO.empty();
        }
        examVOList.forEach(examVO -> examVO.setEnter(true));
        return PageVO.of(examVOList, total);
    }
}
